﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Manatee.Trello;

namespace AIC_Notification
{
    public class AIC_Card
    {
        public string CardName { get; set; }
        public string CardId { get; set; }
        public string Url { get; set; }
        public string Note { get; set; }

        public virtual void CalculateStatistics()
        {
        }
    }

    public class Guidance_MonthlyPlan: AIC_Card
    {
        public int Year;
        public int Month;
        public DateTime SubmitDate;
        public Checklist PlanCheckList = null;

        public Guidance_MonthlyPlan()
        {
        }
    }

    public class Guidance_MeetingRecap: AIC_Card
    {
        public int Year;
        public int Month;
        public int Day;
        public int Number;
        public DateTime SubmitDate;
    }

    public class Guidance_ReportCard : AIC_Card
    {
        public enum GuidanceTerm { Spring, Summer, Fall};

        public int Year;
        public GuidanceTerm Term;
        public bool HasAttachment;
        public string AttachmentName;
        public DateTime SubmitDate;
    }

    public class Essay: AIC_Card
    {
        // Regex patterns
        private static string EssayAtthPattern = @"(?i:E)(?<Number>\d+)";
        private static string DraftAtthPattern = @"(?i:D)(?<Number>\d+)";
        private static string RevisionAtthPattern = @"(?i:R)(?<Number>\d+)";

        //Essay
        public int Number;
        public bool BrainStorm;
        public bool hasDueDate;
        public DateTime DueDate;
        public IList<Attachment> Attachments;
        public int NumberOfLateEssay;

        // Draft Info
        public int DraftNumber;
        public bool hasDraftAttachment;
        public string DraftAttachmentName;
        public DateTime DraftAttachmentDate;

        //Revision Info
        public bool hasRevision;
        public int RevisionNumber;
        public bool hasRevisionAttachment;
        public string RevisionAttachmentName;
        public DateTime RevisionAttachmentDate;

        // Final
        public bool isFinal;

        public Essay(string Id)
        {
            CardName = "";
            CardId = Id;
            Url = "";
            Note = "";
            Attachments = new List<Attachment>();
        }

        public void SetCard(string essayCardName, string essayCardUrl, int number, bool brainstorm, int draftNumber, int revisionNumber, bool Finalized)
        {
            // Record Trello Information 
            CardName = essayCardName;
            Url = essayCardUrl;

            // card = essayCard;
            Number = number;
            BrainStorm = brainstorm;
            DraftNumber = draftNumber;
            if (revisionNumber != -1)
            {
                hasRevision = true;
                RevisionNumber = revisionNumber;
            }
            else
            {
                hasRevision = false;
            }

            isFinal = Finalized;

            hasDraftAttachment = false;
            hasRevisionAttachment = false;

            Global.SysUtil.Log(String.Format("Essay: {0}, Brainstorm: {1}, Draft: {2}, Revision: {3}, Finalized: {4}", 
                Number, BrainStorm ? "Yes" : "No", DraftNumber, hasRevision ? RevisionNumber.ToString() : "None", isFinal ? "True" : "False"));
        }

        public override void CalculateStatistics()
        {
            int NoOfAttachment = 0;

            Global.SysUtil.Log(String.Format("{0} Attachment(s)", NoOfAttachment));

            NoOfAttachment = Attachments.Count;

            // No attachment? 
            if (NoOfAttachment == 0)
            {
                /*
                                if (essay.hasDueDate != true)
                                {
                                    // Use card last activity as draft date
                                    essay.Note += "Missing Due Date for Student; ";
                                    Global.SysUtil.Log(String.Format("Card {0}: Missing Due Date for Student", card.Name), 1, ProcessingBoard.Name, ProcessingList.Name);
                                    essay.hasDraftAttachment = true;
                                    essay.DraftAttachmentName = "";
                                    essay.DraftAttachmentDate = (DateTime)card.LastActivity;
                                }
                */
            }

            // One attachment - Assume Draft
            if (NoOfAttachment == 1)
            {
                bool FoundDraft = ParseDraftAttachment(Attachments[0].FileName, Attachments[0].LastModifiedDate);
                bool FoundRevision = ParseRevisionAttachment(Attachments[0].FileName, Attachments[0].LastModifiedDate);

                if (!FoundDraft && !FoundRevision) // Is neither a draft or a revision
                {
                    // Use attachment date even if draft name not valid
                    Note += "Ambiguous attachment name; Assume is draft; ";
                    //Global.SysUtil.Log(String.Format("Card {0}: Ambiguous attachment name; Assume is draft", ProcessingCard.Name), 1, ProcessingBoard.Name, ProcessingList.Name);
                    hasDraftAttachment = true;
                    DraftAttachmentName = Attachments[0].FileName;
                    DraftAttachmentDate = Attachments[0].LastModifiedDate;
                }

                if (!FoundDraft && FoundRevision) // No draft but found a likely revision
                {
                    if (!hasRevision) // if no revision expected, signal an error.
                    {
                        // Use attachment date even if draft name not valid
                        Note += String.Format("No draft but found revision attachment; Card not marked with /R{0}; ", DraftNumber);
                        //Global.SysUtil.Log(String.Format("Card {0}: No draft but found revision attachment; Card not marked with /R{1}", ProcessingCard.Name, essay.DraftNumber), 1, ProcessingBoard.Name, ProcessingList.Name);
                    }

                    hasDraftAttachment = true;
                    DraftAttachmentName = "";
                    DraftAttachmentDate = Attachments[0].CreationDate;
                }
            }

            // Two attachments
            if (NoOfAttachment >= 2)
            {
                bool foundDarft = false;
                bool foundRevision = false;

                // Find draft attachment
                for (int i = 0; i < NoOfAttachment; i++)
                {
                    foundDarft = ParseDraftAttachment(Attachments[i].FileName, Attachments[i].LastModifiedDate);
                    if (foundDarft)
                        break;
                }

                // If draft cannot be found, find attachment with earliest date
                if (!foundDarft)
                {
                    DateTime date = Attachments[0].LastModifiedDate;
                    int draftAttachmentIndex = 0;
                    for (int i = 0; i < NoOfAttachment; i++)
                    {
                        if (Attachments[i].LastModifiedDate < date)
                        {
                            date = Attachments[i].LastModifiedDate;
                            draftAttachmentIndex = i;
                        }
                    }
                    Note += "Has attachment but draft not found - use earliest date attachment as draft; ";
                    // Global.SysUtil.Log(String.Format("Card {0}: Has attachment but draft not found - use earliest date attachment as draft", ProcessingCard.Name), 1, ProcessingBoard.Name, ProcessingList.Name);
                    hasDraftAttachment = true;
                    DraftAttachmentName = Attachments[draftAttachmentIndex].FileName;
                    DraftAttachmentDate = date;
                }

                // Find revision attachment
                for (int i = 0; i < NoOfAttachment; i++)
                {
                    foundRevision = ParseRevisionAttachment(Attachments[i].FileName, Attachments[i].LastModifiedDate);
                    if (foundRevision)
                        break;
                }

                if (hasRevision) // Expecting revision
                {
                    if (!foundRevision) // Expected revision but not found attachment, try to make up using attachment with largest date.
                    {
                        DateTime date = Attachments[0].LastModifiedDate;
                        int revisionAttachmentIndex = 0;
                        for (int i = 0; i < NoOfAttachment; i++)
                        {
                            if (Attachments[i].LastModifiedDate > date)
                            {
                                hasRevision = true;
                                date = Attachments[i].LastModifiedDate;
                                revisionAttachmentIndex = i;
                            }
                        }
                        Note += "Has attachment but revision not found - use latest date attachment as revision; ";
                        // Global.SysUtil.Log(String.Format("Card {0}: Has attachment but revision not found - use latest date attachment as revision", ProcessingCard.Name), 1, ProcessingBoard.Name, ProcessingList.Name);
                        hasRevisionAttachment = true;
                        RevisionAttachmentName = Attachments[revisionAttachmentIndex].FileName;
                        RevisionAttachmentDate = date;
                    }
                }
                else // Not expecting revision but found one
                {
                    if (foundRevision)
                    {
                        // Global.SysUtil.Log(String.Format("Card {0} missing has revision \"{1}\" but no /R tag", ProcessingCard.Name, essay.RevisionAttachmentName), 1, ProcessingBoard.Name, ProcessingList.Name);
                        hasRevision = true;
                    }
                }
            }

            Global.SysUtil.Log("Attachment Draft Date: " + DraftAttachmentDate);
            if (hasRevision)
                Global.SysUtil.Log("Attachment Revision Date: " + RevisionAttachmentDate);
        }

        private bool ParseAttachmentName(string attachmentName, ref int EssayNumber, ref int DraftNumber, ref int RevisionNumber)
        {
            try
            {
                Regex re = new Regex(EssayAtthPattern, RegexOptions.None, TimeSpan.FromMilliseconds(150));
                Match me = re.Match(attachmentName);
                if (me.Success)
                    EssayNumber = Int32.Parse(me.Groups["Number"].Value);

                Regex rd = new Regex(DraftAtthPattern, RegexOptions.None, TimeSpan.FromMilliseconds(150));
                Match md = rd.Match(attachmentName);
                if (md.Success)
                    DraftNumber = Int32.Parse(md.Groups["Number"].Value);

                Regex rr = new Regex(RevisionAtthPattern, RegexOptions.None, TimeSpan.FromMilliseconds(150));
                Match mr = rr.Match(attachmentName);
                if (mr.Success)
                    RevisionNumber = Int32.Parse(mr.Groups["Number"].Value);

                return md.Success || mr.Success; // Need to at least find a draft or revision #
            }
            catch
            {
                Global.SysUtil.Log("Attachment Parse Error: " + attachmentName);
                return false;
            }
        }

        private bool ParseDraftAttachment(string attachmentName, DateTime? date)
        {
            int AEssayNumber = -1;
            int ADraftNumber = -1;
            int ARevisionNumber = -1;

            Global.SysUtil.Log("Draft Attachment: " + attachmentName);

            if (ParseAttachmentName(attachmentName, ref AEssayNumber, ref ADraftNumber, ref ARevisionNumber))
            {
                if (AEssayNumber != -1)
                    if (Number != AEssayNumber)
                    {
                        Note += "Card Essay # not equals to Attachment Essay #; ";
                        // Global.SysUtil.Log(String.Format("Card {0}: Card Essay # not equals to Attachment Essay #", essay.CardName), 1, ProcessingBoard.Name, ProcessingList.Name);
                    }


                if (ADraftNumber != -1 && ARevisionNumber == -1) // Has draft # but not revision #
                {
                    if (DraftNumber == ADraftNumber)
                    {
                        hasDraftAttachment = true;
                        DraftAttachmentName = attachmentName;
                        DraftAttachmentDate = (DateTime)date;
                        Global.SysUtil.Log("Draft Attachment Matched: " + attachmentName);
                        return true;
                    }
                    else
                    {
                        Note += "Card Draft # not equals to Attachment Draft #; ";
                        // Global.SysUtil.Log(String.Format("Card {0}: Card Draft # not equals to Attachment Draft #", essay.CardName), 1, ProcessingBoard.Name, ProcessingList.Name);
                    }
                }
            }

            return false;
        }

        private bool ParseRevisionAttachment(string attachmentName, DateTime? date)
        {
            int AEssayNumber = -1;
            int ADraftNumber = -1;
            int ARevisionNumber = -1;

            Global.SysUtil.Log("Revision Attachment: " + attachmentName);

            if (ParseAttachmentName(attachmentName, ref AEssayNumber, ref ADraftNumber, ref ARevisionNumber))
            {
                if (AEssayNumber != -1)
                    if (Number != AEssayNumber)
                    {
                        Note += "Card Essay # not equals to Attachment Essay #; ";
                        // Global.SysUtil.Log(String.Format("Card {0}: Card Essay # not equals to Attachment Essay #", essay.CardName), 1, ProcessingBoard.Name, ProcessingList.Name);
                    }

                if (ARevisionNumber != -1)
                {
                    if (ARevisionNumber == DraftNumber) // Notice: we just need to match draft #
                    {
                        hasRevisionAttachment = true;
                        RevisionAttachmentName = attachmentName;
                        RevisionAttachmentDate = (DateTime)date;
                        Global.SysUtil.Log("Revision Attachment Matched: " + attachmentName);
                        return true;
                    }
                    else
                    {
                        Note += String.Format("Revision Attachment R{0} not equals to Card draft D{1}; ", ARevisionNumber, DraftNumber);
                        // Global.SysUtil.Log(String.Format("Card {0}: Revision attachment # R{1} not equals to Card draft # D{2}", essay.CardName, ARevisionNumber, essay.DraftNumber), 2, ProcessingBoard.Name, ProcessingList.Name);
                    }
                }
            }

            return false;
        }
    }

    public class Task: AIC_Card
    {
        // Has a list of checklists
        public IList<Checklist> TaskLists;

        // Date range where this list applies
        public DateTime StartTracking { get; set; }
        public DateTime EndTracking { get; set; }

        public Task(string Id)
        {
            CardName = "";
            CardId = Id;
            Url = "";
            Note = "";

            TaskLists = new List<Checklist>();
        }

        public void SetCard(string taskCardName, string taskCardUrl)
        {
            // Record Trello Information 
            CardName = taskCardName;
            Url = taskCardUrl;

            Global.SysUtil.Log(String.Format("Task: {0}", CardName));
        }

    }

    public class Checklist
    {
        public string ChecklistName { get; set; }
        public string Note { get; set; }
        public IList<CheckItem> Items;

        public Checklist()
        {
            ChecklistName = "";
            Note = "";
            Items = new List<CheckItem>();
        }

        public void ReadListItems(Manatee.Trello.CheckList cl)
        {
            int ItemCount = 0;

            foreach (var Item in cl.CheckItems)
            {
                CheckItem ci = new CheckItem();
                ci.CheckItemName = Item.Name;
                ci.Number = ItemCount;
                ci.State = Item.State == CheckItemState.Complete ? true : false;

                Items.Add(ci);
                ItemCount += 1;
            }
        }

        public int TotalItems()
        {
            return Items.Count;
        }

        public int ItemsComplete()
        {
            int CompleteCount = 0;

            foreach (var Item in Items)
            {
                if (Item.State == true)
                    CompleteCount += 1;
            }

            return CompleteCount;
        }
    }

    public class CheckItem
    {
        public string CheckItemName { get; set; }
        public int Number { get; set; }
        public bool State { get; set; }
        public string Note { get; set; }

        public CheckItem()
        {
            CheckItemName = "";
            Number = 0;
            State = false;
            Note = "";
        }
    }
}
